﻿

namespace Tentacle.Downloader
{
    using System;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading.Tasks;

    using static Tentacle.Utils.TentacleProtocol;
    using static Tentacle.Utils.Hepler;

    using Disposabled = System.Boolean;
    using SocketTask = System.Threading.Tasks.Task;
    using SocketTaskState = System.Boolean;
    using SocketConntectionState = System.Boolean;

    sealed class ServerService : IServerService
    {
        Socket              _socket;
        SocketTask          _task;
        SocketTaskState     _state;
        Disposabled         _disposed;
        SocketConntectionState  _connected;
        byte[]              _buffer;

        public event Action<string> SetToken;
        public event Func<string,Task<string>> SetSite;
        public event Action Disconnect;

        #region Constructor

        /// <summary>
        /// 
        /// </summary>
        public ServerService() : this(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="addressFamily"></param>
        /// <param name="socketType"></param>
        /// <param name="protocolType"></param>
        public ServerService(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
        {
            var ips = from e in Dns.GetHostAddresses(Dns.GetHostName()) where e.AddressFamily == addressFamily select e;
            if (ips.Count() == 0) {
                throw new ArgumentException("无法绑定本地IP地址");
            }

            var ip = ips.First();

            _socket = new Socket(addressFamily, socketType, protocolType);

            _socket.Bind(new IPEndPoint(ip, 0));
            _socket.Listen(2);
            _buffer = new byte[4096];
        }

        #endregion

        #region Public Method

        public void Start()
        {
            if (!_state) {
                _state = true;
                _task = SocketTask.Factory.StartNew(SocketMainLoop);
            }
        }

        public void Dispose()
        {
            if (!_disposed) {
                _state = false;
                _task.Dispose();
                _socket.Dispose();
                _disposed = true;
            }

        }

        #endregion

        #region Property

        public string Guid {
            get {
                return System.Guid.NewGuid().ToString();
            }
        }

        public bool Connected {
            get {
                return _connected;
            }
        }

        public IPEndPoint LocalEndPoint {
            get {
                return (IPEndPoint)_socket?.LocalEndPoint;
            }
        }





        #endregion

        async void SocketMainLoop()
        {
            while (_state) {
                var client = _socket.Accept();
                _connected = true;
                while (client.Connected) {
                    try {
                        var count = client.Receive(_buffer);
                        var msg = Encoding.UTF8.GetString(_buffer,0,count)
                                               .Replace("<","")
                                               .Replace(">","");
                        // Trace
                        Trace(msg);

                        switch (msg) {
                            case Utils.TentacleProtocol.Disconnect:
                                client.Disconnect(true);
                                _socket.Disconnect(true);
                                break;
                            default:
                                await ParseCommand(msg, client);
                                break;
                        }
                    }
                    catch (Exception ex) {
                        Trace(ex);
                        client.Dispose();
                        _connected = false;
                    }

                }
            }
        }

        async SocketTask ParseCommand(string content, Socket socket)
        {
            if (content.Length < 10) {
                return;
            }

            var command = content.Substring(0,9);
            switch (command) {
                case "SET-TOKEN": {
                        // Set-Token
                        var token = content.Substring(10,content.Length - 10);

                        Response(SetToken == null ? Response_ERROR : Response_SUCCESS, socket);
                        SetToken?.Invoke(token);
                    }
                    break;
                case "SET-SITE,": {
                        // Set-Site
                        var site = content.Substring(9,content.Length - 9);
                        if (string.IsNullOrEmpty(site)) return;
                        try {
                             var page = await SetSite(site);
                            Response(page, socket);
                        }
                        catch (Exception ex) {
                            Trace(ex);
                            ResponseError(socket);
                        }
                    }
                    break;
            }
        }

        void ResponseError(Socket socket)
        {
            Response(Response_ERROR, socket);
        }

        void Response(string content, Socket socket)
        {
            var array = Encoding.UTF8.GetBytes(content);

            var length = BitConverter.GetBytes(array.Length);
            Trace(array.Length);

            // Send Header
            socket.Send(length);

            // Receive Feedback
            var count = socket.Receive(_buffer);
            var msg = Encoding.UTF8.GetString(_buffer,0,count);

            // Send Data
            if (msg == Response_SUCCESS) {
                socket.Send(array);
            }
        }
    }
}
